/*
* Copyright (C) 2021 ~ 2022 Uniontech Technology Co., Ltd.
*
* Author:      zhangbingyi <zhangbingyi@uniontech.com>
*
* Maintainer:  zhangbingyi <zhangbingyi@uniontech.com>
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

#include "usbimportandexportjob.h"

#include <unistd.h>

#include <QTextStream>
#include <QDir>
#include <QThread>

// 白名单导入数据校验
#define WHITELIST_TEMPLATE_COL_COUNT 5
#define WHITELIST_TEMPLATE_ITEM_CHAR_MAX_LENTH 256

USBImportAndExportJob::USBImportAndExportJob()
{
}

USBImportAndExportJob::~USBImportAndExportJob()
{
}

// 确认导出全部连接记录
void USBImportAndExportJob::onExportAllDevConnetionRecord(const QString &path, const USBConnectionInfoList &list)
{
    // 导出日志
    exportConnetionRecord(path, list);

    // 通知导出全部设备的全部连接记录完成
    Q_EMIT exportAllDevConnectionRecordFinished();
}

void USBImportAndExportJob::onExportOneDevConnetionRecord(const QString &path, const USBConnectionInfoList &list)
{
    // 导出日志
    exportConnetionRecord(path, list);

    // 通知导出某一个设备的全部连接记录完成
    Q_EMIT exportOneDevConnectionRecordFinished();
}

void USBImportAndExportJob::onExportWhitelistTemplate(const QString &path)
{
    if (path.isEmpty()) {
        qDebug() << Q_FUNC_INFO << "export canceled";
        return;
    }

    QFile file(path);
    if (!file.open(QIODevice::OpenModeFlag::WriteOnly)) {
        qDebug() << Q_FUNC_INFO << path << "open failed";
        return;
    }

    // 数据流
    QTextStream ts(&file);
    // 向模板中载入表头信息
    ts << QString("\"%1\",").arg(tr("Device Name (Optional)"))
       << QString("\"%1\",").arg(tr("SN"))
       << QString("\"%1\",").arg(tr("PID"))
       << QString("\"%1\",").arg(tr("VID"))
       << QString("\"%1\"\n").arg(tr("Policy (Enter a number, 0 means read & write, 1 means read-only)"));

    fsync(file.handle()); // 同步到磁盘
    file.close();
}

void USBImportAndExportJob::onImportUSBWhitelist(const QString &path)
{
    // 标志位
    bool isSucceed = true;

    USBConnectionInfoList list;
    if (path.isEmpty()) {
        qDebug() << Q_FUNC_INFO << "file path invalid!";
    }

    QFile file(path);

    if (!file.open(QIODevice::OpenModeFlag::ReadOnly)) {
        qDebug() << Q_FUNC_INFO << path << "opened failed!";
    }

    QTextStream ts(&file);
    ts.readLine(); // 不读取第1行的表头内容

    QStringList csvList;
    while (!ts.atEnd()) {
        const QString &lineStr = ts.readLine();
        csvList.append(lineStr);
    }

    file.close(); // close csv文件

    for (int i = 0; csvList.size() > i; ++i) {
        const QString &lineStr = csvList.at(i);
        const QStringList &infoStrList = lineStr.split(",");

        // 其他场景错误
        if (5 < infoStrList.size()) {
            Q_EMIT importUSBWhitelistFailed(); // 其他场景错误信号
            isSucceed = false;
            break;
        }

        if (isSucceed) {
            // 校验没有写的情况
            switch (infoStrList.size()) {
            case 0:
                Q_EMIT usbWhitelistInfoVerifyFailed(i + 1, tr("Device")); // 发送导入失败信号
                list.clear(); // 清除list
                isSucceed = false;
                break;

            case 1:
                Q_EMIT usbWhitelistInfoVerifyFailed(i + 1, tr("SN")); // 发送导入失败信号
                list.clear(); // 清除list
                isSucceed = false;
                break;
            case 2:
                Q_EMIT usbWhitelistInfoVerifyFailed(i + 1, tr("PID")); // 发送导入失败信号
                list.clear(); // 清除list
                isSucceed = false;
                break;
            case 3:
                Q_EMIT usbWhitelistInfoVerifyFailed(i + 1, tr("VID")); // 发送导入失败信号
                list.clear(); // 清除list
                isSucceed = false;
                break;
            case 4:
                Q_EMIT usbWhitelistInfoVerifyFailed(i + 1, tr("Policy")); // 发送导入失败信号
                list.clear(); // 清除list
                isSucceed = false;
                break;
            }
        }

        if (isSucceed) {
            QString name = infoStrList.at(0);
            QString serial = infoStrList.at(1);
            QString pid = infoStrList.at(2);
            QString vid = infoStrList.at(3);
            QString permModeStr = infoStrList.at(4);
            bool ok = true;
            const int permMode = permModeStr.toInt(&ok);

            // 校验内容字符长度和内容是否为空
            if (WHITELIST_TEMPLATE_ITEM_CHAR_MAX_LENTH < name.size()) {
                Q_EMIT usbWhitelistInfoVerifyFailed(i + 1, tr("Device")); // 发送导入失败信号
                list.clear(); // 清除list
                isSucceed = false;
                break;
            }
            if (WHITELIST_TEMPLATE_ITEM_CHAR_MAX_LENTH < serial.size() || serial.isEmpty()) {
                Q_EMIT usbWhitelistInfoVerifyFailed(i + 1, tr("SN")); // 发送导入失败信号
                list.clear(); // 清除list
                isSucceed = false;
                break;
            }
            if (WHITELIST_TEMPLATE_ITEM_CHAR_MAX_LENTH < pid.size() || pid.isEmpty()) {
                Q_EMIT usbWhitelistInfoVerifyFailed(i + 1, tr("PID")); // 发送导入失败信号
                list.clear(); // 清除list
                isSucceed = false;
                break;
            }
            if (WHITELIST_TEMPLATE_ITEM_CHAR_MAX_LENTH < vid.size() || vid.isEmpty()) {
                Q_EMIT usbWhitelistInfoVerifyFailed(i + 1, tr("VID")); // 发送导入失败信号
                list.clear(); // 清除list
                isSucceed = false;
                break;
            }
            if (!ok || ((USB_MANAGER_PERM_MODE_RW != permMode) && (USB_MANAGER_PERM_MODE_RO != permMode))) {
                Q_EMIT usbWhitelistInfoVerifyFailed(i + 1, tr("Policy")); // 发送导入失败信号
                list.clear(); // 清除list
                isSucceed = false;
                break;
            }
            USBConnectionInfo info;
            info.name = name;
            info.serial = serial;
            info.productID = pid;
            info.permMode = permMode;
            info.addedTime = QDateTime::currentDateTime().toString(USB_MANAGER_CONN_TIME_FORMATION);

            // vendorID 需要正则匹配
            // bug128029 -- 白名单导入时VID字段值超过8个字符，导入成功后白名单详情页面VID值显示错误
//            QRegularExpression re("(^0*([1-9 a-f A-F]{1,3}$))|(^0*([1-9 a-f A-F][0-9 a-f A-F]{1,3})$)");
//            QRegularExpressionMatch match = re.match(vid);
//            if (match.hasMatch()) {
//                info.vendorID = match.captured(0);
//            } else {
                // 未能经过正则校验
                Q_EMIT usbWhitelistInfoVerifyFailed(i + 1, tr("VID")); // 发送导入失败信号
                list.clear(); // 清除list
                isSucceed = false;
                break;
//            }
            list.append(info);
        }
    }

    if (isSucceed) {
        // 通知导入的白名单校验通过   有一个标志位判断  中途被break则至为false
        Q_EMIT usbWhitelistVerifySucceed(list);
    }
}

void USBImportAndExportJob::exportConnetionRecord(const QString &path, const USBConnectionInfoList &list)
{
    // 打开 导出路径
    QFile file(path);

    if (!file.open(QIODevice::OpenModeFlag::WriteOnly)) {
        qDebug() << Q_FUNC_INFO << path << "opened failed!";
        return;
    }

    QTextStream ts(&file);

    ts << QString("\"%1\",").arg(tr("Device"))
       << QString("\"%1\",").arg(tr("SN"))
       << QString("\"%1\",").arg(tr("PID"))
       << QString("\"%1\",").arg(tr("VID"))
       << QString("\"%1\",").arg(tr("Time Connected"))
       << QString("\"%1\"\n").arg(tr("Policy"));

    for (int i = list.size() - 1; i >= 0; --i) {
        USBConnectionInfo info = list.at(i);

        QString trategy;
        int mode = info.permMode;
        if (0 == mode) {
            trategy = (tr("Read-write"));
        } else if (1 == mode) {
            trategy = (tr("Read only"));
        } else {
            trategy = (tr("Block"));
        }
        ts << tr(((info.name).toStdString().c_str())) + ","
           << info.serial + ","
           << info.productID + ","
           << info.vendorID + ","
           << info.addedTime + ","
           << (trategy + "\n");
    }

    fsync(file.handle());
    file.close();
}
